﻿#include "func_storage.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include "mp4_pkt.h"
#include "transfer_size.h"
typedef struct _pkt_mp4_fileinfo_
{
    int ch;
    time_t tm_start;
    time_t tm_end;
    int width;
    int height;
    char   path[64]; 
}pkt_mp4_fileinfo;

char  g_dest_disk[64] = "/dev/mmcblk0";
int  g_ch_num = 1;
pkt_mp4_fileinfo g_mp4_finfo;

void * storage_entry(void * arg);
void * pkt_mp4_thd(void * arg);
//回调函数
static void STORAGE_STATUS_CallBackHandle(uint32_t id, const char* param, uint32_t paramLen)
{
    CBAK_REC_STATUS *stRecStatus = NULL;

    fprintf(stderr, "ID[0x%x]\n", id);
    if(id == CALLBACK_ID_FS_RECORD_STATUS)
    {
        stRecStatus = (CBAK_REC_STATUS *)(param);
        fprintf(stderr, "ch[%d] recType[%d] diskLeaf[%d MB]\n", stRecStatus->ch, stRecStatus->recType, stRecStatus->diskLeaf);
    } 
}

int  start_storage(int ch_nums,char * disk_dev)
{
    //获取参数
    g_ch_num = ch_nums;

    if(disk_dev != NULL && strlen(disk_dev) > 4)
    {
        strcpy(g_dest_disk,disk_dev);
    }
    pthread_t stoage_thread;

    pthread_create(&stoage_thread, NULL, (void*(*)(void*))storage_entry, NULL);
    printf("start storage func.\n");

    return 0;
}


#ifdef  _WIN32
typedef CRITICAL_SECTION     cross_lock_t;
typedef  HANDLE				cross_thread_t;			//线程ID
typedef  LPTHREAD_START_ROUTINE	cross_start_routine;	//线程函数
static void sleep(int secs)
{
    Sleep(secs * 1000);
}
#else
#include <pthread.h>
typedef pthread_mutex_t     cross_lock_t;
typedef  pthread_t		    cross_thread_t;			        //线程ID
typedef  void *(*start_routine)(void*);
typedef  start_routine		cross_start_routine;		//线程函数
#endif

int g_time_zone = 0;
int g_source_type[MAIN_VI_NUMS] = { 0 };


int print_unix_time( time_t curTm, char *title)
{
    struct tm* timenow;
    timenow = localtime(&curTm);
    fprintf(stderr, "%s %04d-%02d-%02d %2d:%2d:%2d ctime %ld\n",
            title,
            timenow->tm_year+1900, timenow->tm_mon+1, timenow->tm_mday,
            timenow->tm_hour,timenow->tm_min,timenow->tm_sec,curTm);
    return 0;
}



//输入参数：start_routine 线程的执行函数
//			pParam  线程执行时传入的参数
//			pRun    线程是否执行的bool量的指针，如果为NULL，则不理会此参数
//return value: 成功：返回线程ID    *pRun = true
//				失败：返回PUB_CREATE_THREAD_FAIL  *pRun的值不变
cross_thread_t Cross_CreateThread(cross_start_routine start_routine, void* pParam, int *runFlag,int stackSize)
{
    cross_thread_t threadID;
    if (NULL != runFlag)
    {
        *runFlag = 1;
    }
#ifdef _WIN32
    threadID = CreateThread(0, 0, start_routine, (LPVOID)pParam, 0, NULL);
    if ( (NULL == threadID) && (NULL != runFlag) )
    {
        *runFlag = 0;
    }
#elif defined __linux
    int iRet = 0;
    //pthread_t tid;
    pthread_attr_t attr;
    pthread_attr_init( &attr);
    pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED);
    pthread_attr_setstacksize( &attr, stackSize );
    if( (iRet = pthread_create( &threadID, &attr, start_routine, pParam)) != 0 )
    {
        threadID = 0;
        if (NULL != runFlag)
        {
            *runFlag = 0;
        }
    }
    errno = iRet;	//线程创建不会设置错误码，而是直接返回错误码
#endif
    return threadID;
}
 


void * storage_entry(void * arg)
{
    StorageConfig storageCfg;
    int ch = 0;
    time_t cur_tm = 0;
    char local_time[128];
    char gmt_time[128];
    struct tm *timenow = NULL;
    int time_zone = 0;
    int local_hour, utc_hour;
    int data_water = 0;
    unsigned int file_index = 0;
    unsigned int file_sn = 0;
    int file_percent = 0;
    time_t max_rec_tm = 0;
    int ret = 0;
    int  i  = 0;

    uint32_t blk_sz = 64;
    uint32_t part1_sz = 2048;
    uint32_t use_sz = 0; //0 表示全部使用
    char cmd[128] = {0};

    time(&cur_tm);
    timenow = localtime(&cur_tm);
    local_hour = timenow->tm_hour;
    print_unix_time(cur_tm,"local time");

    timenow = gmtime(&cur_tm);
    utc_hour   = timenow->tm_hour;
    print_unix_time(cur_tm,"utc gmtime");

    //计算出来 时区
    time_zone = local_hour - utc_hour;

    fprintf(stderr,"localtime %s  gmttime %s. utc_tm %d  time_zone %d \n",local_time,gmt_time,(int)cur_tm,time_zone);

    g_time_zone = time_zone;

    memset(&storageCfg,0,sizeof(storageCfg));
    fprintf(stderr,"support SDcord record storageCfg.channelNum %d .\n",g_ch_num );

    //通道数量
    storageCfg.channelNum = g_ch_num;

    storageCfg.bCycleRec  = 1;
    storageCfg.diskNum    = 1;
    storageCfg.bPrepare   = 1;
    storageCfg.bNoneMixAudio = 1;  //不去混合获取音视频数据

    //缓存配置 
    storageCfg.main_media_cache = SIZE_3MB;   //视频缓存
    storageCfg.buf_sz_frame    = SIZE_KB(300);  //搬运视频缓存
	storageCfg.buf_sz_flush    = SIZE_1MB + SIZE_512KB; //刷新对齐缓存 
    storageCfg.buf_sz_replay   = SIZE_1MB + SIZE_512KB; // 回放缓存 
    storageCfg.scan_drv_no  = 1;
    storageCfg.manualRecBIT = -1; //默认开启所有通道

    STFS_Init(storageCfg);//初始化
    STFS_set_debuglevel(0);

	if (!STFS_DISK_IS_INIT(g_dest_disk))
	{  //未格式化，
	 	printf("DISK_IS_INIT not init format blk sz %d MB part_sz %d MB use_sz %d MB.\n", blk_sz, part1_sz, use_sz);
 	    ret = STFS_FormatToRecdisk(g_dest_disk, blk_sz, part1_sz, use_sz);
        if (ret == 0)
        { 
           printf("Format disk %s ok.\n", g_dest_disk);
           system("reboot");
        }
	}
    
    if(-1 == access("/mnt/sdp1",0))
        system("mkdir -p /mnt/sdp1");
    
    if(-1 == access("/mnt/sdp2",0))
        system("mkdir -p /mnt/sdp2");  

    if(0 == access("/dev/mmcblk0p2",0))
    { 
        system("mount /dev/mmcblk0p2 /mnt/sdp2");
    }

	STFS_Buffer_Change(g_source_type, g_ch_num);

    //先使硬盘测试一下，后续会修改成SD卡
    max_rec_tm = STFS_GetRecMaxTime();

    printf("max_rec_tm %d , cur_tm %d .\n",(int)max_rec_tm,(int)cur_tm);
    timenow = localtime(&max_rec_tm);
    local_hour = timenow->tm_hour;
    sprintf(local_time, "record localtime %4d-%02d-%02d %02d:%02d:%02d",  timenow->tm_year+1900,timenow->tm_mon+1,timenow->tm_mday,timenow->tm_hour, timenow->tm_min, timenow->tm_sec);
    printf("storage record time is  %s .\n",local_time);

    //设置回调函数
    STFS_SET_STATUS_CBAK(STORAGE_STATUS_CallBackHandle);
    STFS_RecordSw(1); //录像总开关

    ret = STFS_RecordStart();
    printf("Storage start record ret %d .\n", ret);

    while(1) 
    {
        sleep(10);
		int isSub = 0;
        //循环查看录像信息
        for (ch = 0; ch < storageCfg.channelNum; ch++)
        {
            data_water =  STFS_FifoGetFrameWater(ch,0);
            file_percent = STFS_GetRecordPercent( ch);
            file_index = STFS_GetRecordIndex(ch);
            file_sn =  STFS_GetRecordSn(ch);

            if(file_percent > 1)
            {
             //   printf("ch %02d index %05d file SN %05d percent %d  warter %d \n",
             //              ch,file_index,file_sn,file_percent,data_water);
            }
        }
    }

    STFS_RecorcStop();
    printf("storage thread exit.\n");
    return NULL;
}

int  pkt_mp4(char * file_path, time_t tm_start,time_t tm_end,int width,int height,int ch)
{
    pthread_t pktmp4_thread;


    memset(&g_mp4_finfo,0,sizeof(pkt_mp4_fileinfo));

    g_mp4_finfo.ch =ch;
    g_mp4_finfo.tm_start = tm_start;
    g_mp4_finfo.tm_end = tm_end;
    g_mp4_finfo.width = width;
    g_mp4_finfo.height = height;

    pthread_create(&pktmp4_thread, NULL, (void*(*)(void*))pkt_mp4_thd, (void*)&g_mp4_finfo);
    printf("start storage func.\n");

    return 0;
}


//打包文件
void * pkt_mp4_thd(void * arg)
{
    pkt_mp4_fileinfo * pkt_info = (pkt_mp4_fileinfo*)arg;

    static int is_pkting = 0;

    if(!is_pkting)
    {
        printf("is pkting mp4 file.\n");
        return NULL;
    }
    
    //打开文件

    //循环读取视频 并打包mp4

    //获取第一帧I帧

	int ret = 0;
	int frm_type = 0;
	int frm_len = 0;
	printf("task_pkt ch %d  start.\n", pkt_info->ch);
	char log_tmp[128] = { 0 };
	int head_len = 0;
	int codec_t  = 0;
	void * pData = NULL;
	int64_t cur_pts = 0;
	int64_t	last_pts = 0;
	int64_t diff = 0;
    int64_t pkt_ms = 0;
	int buf_size = 400 * 1024; //此处根据最大I帧设计
	unsigned char* s_buffer = (unsigned char*) malloc(buf_size);// new unsigned char[buf_size];
	unsigned char s_extra_data[64 * 1024] = {0};
    unsigned char * pBuf =  (unsigned char*) malloc(buf_size); //new unsigned char[buf_size];
	int frm_cnt = 0;
	int is_key = 0;
    int is_need_Ifrm = 1;
	struct mov_h264_test_t ctx;
	int vcl = 0;
	int update = 0;
	int  n = 0;
	int show_cnt = 5;
    RD_FRAME_H * pFrame = NULL;
    int rate = 1;
    memset(&ctx, 0, sizeof(ctx));
    ctx.track = ctx.trackAudio = -1;
    ctx.width = pkt_info->width;//宽
    ctx.height =pkt_info->height ;//高 需要指定，否则在IOS中，有可能出现花屏问题
    struct mpeg4_aac_t aac;
    uint64_t samples = 0;
    int64_t pts = 0;

    FILE * fileFd = fopen(pkt_info->path, "wb+");
    if(fileFd == NULL )
    {
        printf("packet mp4 fail open  %s .\n",pkt_info->path);
        goto END_PKT;
    }
    printf("start fopen %s ok.\n",pkt_info->path);
    ctx.mov = mov_writer_create(mov_file_buffer(), fileFd, MOV_FLAG_FASTSTART);
    if (ctx.mov == NULL)
    {
        printf("mov_writer_create open file failed.\n");
        goto END_PKT;
    }	
    int client_id = 0; //如果同时有回放，这个ID需要与回放不同

    ret = STFS_PlaybackBytime(pkt_info->tm_start,pkt_info->tm_end,pkt_info->tm_start,pkt_info->ch,client_id); 
    
    printf("start read frame data seek ret %d.\n",ret);
	while (1)
	{
		ret = STFS_ReplayFifoReadFrame(pBuf, buf_size, pkt_info->ch, client_id);
		if (ret <= 0)
		{	
			if(ret == -97)
			{
                pFrame = (RD_FRAME_H*)pBuf;
                frm_type = pFrame->frameType;
                frm_len = pFrame->dataLen;
                cur_pts = pFrame->pts;
				if(cur_pts > (int64_t)pkt_info->tm_end*1000)
				{
					printf("pkt end.\n");
					goto END_PKT;	
				}
			}
			usleep(30*1000);
			continue;
		}

        

		pFrame = (RD_FRAME_H*)pBuf;
		frm_type = pFrame->frameType;
		frm_len = pFrame->dataLen;
		cur_pts = pFrame->pts;
		head_len = pFrame->headLen;

		is_key = (frm_type == IFrame) ? 1 : 0;
		if (is_key)
		{
			is_need_Ifrm = 0;
		}
		else
		{
			if (is_need_Ifrm)
			{
				continue;
			}
		}


		codec_t = pFrame->codec_type;// frmTool.GetCodecType();
		pData = pBuf + head_len;
        if (frm_type== AFrame)
        {

            if (-1 ==  ctx.trackAudio)
            {
                int extra_data_size = mpeg4_aac_audio_specific_config_save(&aac, s_extra_data, sizeof(s_extra_data));
                rate = mpeg4_aac_audio_frequency_to((enum mpeg4_aac_frequency)aac.sampling_frequency_index);
                ctx.trackAudio = mov_writer_add_audio(ctx.mov, MOV_OBJECT_AAC, aac.channel_configuration, 16, rate, s_extra_data, s_extra_data);
                if (-1 ==  ctx.trackAudio) 
                    continue;
            }

            samples += 1024; // aac frame
            int framelen = frm_len;//((pData[3] & 0x03) << 11) | (pData[4] << 3) | (pData[5] >> 5);
            mov_writer_write(ctx.mov,  ctx.trackAudio, pData + 7, framelen - 7, pts, pts, 0);
            pts = samples * 1000 / rate;
        }
		else
        {
            n = h264_annexbtomp4(&ctx.avc, pData, frm_len, s_buffer, buf_size, &vcl, &update);

            if (!init_av_track(&ctx, codec_t, s_extra_data))
            { 
                continue;
            }

            ret = mov_writer_write(ctx.mov, ctx.track, s_buffer, n, ctx.pts, ctx.pts, 1 == vcl ? MOV_AV_FLAG_KEYFREAME : 0);
            if (ret < 0)
            {
                printf("------ Err ----WriteH264VideoData  ret %d frame_sz %d frmType %c \n", ret, frm_len, frm_type);
                frm_cnt = 0;
                goto END_PKT;
            }

            if (last_pts != 0 && frm_cnt != 0)
            {
                diff = abs(cur_pts - last_pts);// -cur_pts;
            }
            else
                diff = 100;
            ctx.pts += diff;
            ctx.dts += diff;
        }

		
		frm_cnt++;
		last_pts = cur_pts;
	}

END_PKT:
    if(ctx.mov != NULL)
	    mov_writer_destroy(ctx.mov);
    if(fileFd != NULL )
    {
        fclose(fileFd);
    }
    free(s_buffer);
    free(pBuf);

    printf(" task_pkt end.\n");

    return NULL;
}

